from Products.Archetypes.public import BaseContentMixin, ReadOnlyStorage, ObjectField
from Products.Archetypes.Renderer import renderer
from AccessControl import ClassSecurityInfo
from BTrees.IOBTree import IOBTree
from types import IntType, StringType, ListType, TupleType

from Products.ArchSurvey.questions.schemata import BaseSurveyQuestionSchema, BaseSurveyItemsQuestionSchema
from Products.ArchSurvey.Permissions import *

class BaseSurveyQuestion(BaseContentMixin):
    """A Basic Question"""
    __implements__ = BaseContentMixin.__implements__
    security = ClassSecurityInfo()
    schema = BaseSurveyQuestionSchema
    
    meta_type = 'BaseSurveyQuestion'
    archetype_name = 'Base Survey Question'
    typeDescription= ''
    typeDescMsgId  = ''
    fieldType = ''
    widgetType=''
    global_allow = 0
    validators = []
    widgetProps = {}
    fieldProps = {}

    actions = ({
        'id'          : 'long_results',
        'name'        : 'Results',
        'action'      : 'string:${object_url}/question_results',
        'permissions' : (VIEW_CONTENT_PERMISSION,)
        },)
    
    def __init__(self, id, schemata, **kwargs):
        self._storage = IOBTree()
        self.field = self.fieldType(id,storage=ReadOnlyStorage(),schemata=schemata)
        self.setSchemata(schemata)
        BaseContentMixin.__init__(self, id, **kwargs)

    def manage_afterClone(self, item):
        """When we're cloned, save the question but not the answers."""
        self.clearResults()
        BaseContentMixin.manage_afterClone(item)
            
    security.declareProtected(ACCESS_CONTENTS, 'Title')
    def Title(self):
        """The title of the question"""
        try:
            return self.getLabel()
        except AttributeError:
            return ""

    def getPages(self):
        return self.aq_parent.getPages()
            
    security.declareProtected(EDIT_CONTENT_PERMISSION, 'clearResults')
    def clearResults(self):
        """Clears results (e.g. to remove tests)"""
        self._storage = IOBTree()
    
    security.declareProtected(EDIT_CONTENT_PERMISSION, 'getInternalFields')
    def getInternalFields(self,type_name=None):
        """Return all the important fields, given my parent's type."""
        if type_name is None:
            type_name = self.aq_parent.meta_type
        def isImportant(x):
            return type_name in getattr(x,'isImportantFor',[])
        return self.Schema().filterFields(isImportant)
            
    security.declareProtected(EDIT_CONTENT_PERMISSION, 'edit')
    def edit(self, **kwargs):
        """Modify our internal fields, then use them to create the resulting Field
        """
        BaseContentMixin.update(self,**kwargs)
        self._regenerate_field(**kwargs)
        
    security.declareProtected(EDIT_CONTENT_PERMISSION, 'edit')
    def processForm(self, **kwargs):
        """Catch this in case the field is edited on it's own."""
        BaseContentMixin.processForm(self,**kwargs)
        self._regenerate_field(**kwargs)

    security.declarePrivate('_regenerate_field')
    def _regenerate_field(self,**kwargs):
        """Regenerate our internal field"""
        fieldkws = self.fieldProps.copy()
        widgetkws = self.widgetProps.copy()
        sch = self.Schema()
        for field in sch.filterFields(buildingBlockFor='field'):
            name = field.getName()
            value = self[name]
            if value is not None:
                fieldkws[name] = value
        for field in sch.filterFields(buildingBlockFor='widget'):
            name = field.getName()
            accessor = getattr(self, sch[name].accessor)
            if accessor is not None:
                accessor = accessor()
            value = kwargs.get(name, accessor)
            if value is not None:
                widgetkws[name] = value
        widget = self.widgetType(**widgetkws)
        self.field = self.fieldType(self.id,
                                    widget=widget,
                                    storage=ReadOnlyStorage(),
                                    write_permission = TAKE_SURVEY,
                                    **fieldkws)

    def accessor(self,uid=None):
        """Gets the results from one particular survey, or
        default if none specified."""
        if isinstance(uid,StringType):
            uid = int(uid)
        if isinstance(uid,IntType):
            return self._storage.get(uid, self.field.default)
        return self.field.default

    def isBinary(self, key=None):
        """Whoa there!  Dangerous!  This is necessary, because we are passing ourself
        as the "instance" of our field.  Therefore, if we are being asked as a 'field',
        it won't have a key, with a key, it means we are being asked as an 'instance'"""
        if key is None:
            return False
        else:
            return BaseContentMixin.isBinary(self, key)
        
    
    security.declareProtected(TAKE_SURVEY, 'render')
    def render(self, mode='edit',uid=None):
        """Render question."""
        return renderer.render(self.getId(), #fieldName
                            mode,  #mode
                            self.field.widget, #widget
                            self, #instance
                            self.field, #field
                            lambda: self.accessor(uid)) #accessor

    security.declareProtected(TAKE_SURVEY, 'addAnswer')
    def addAnswer(self, uid, answer):
        """store an answer to this question of the survey
        uid is a unique identifier that identifies a survey 'instance'
        The Survey itself is in charge of (optionally) keeping track of 
        which user corresponds to which uid, and of varying uids in case
        they shouldn't be matched to users.
        """
        s = self._storage
        if s.has_key(uid):
            del(s[uid])
        s.insert(uid,answer)
        
    def isLabel(self):
        """Returns true if this question is a label type"""
        return False
    
    security.declareProtected(VIEW_RESULTS, 'getResults')
    def getResults(self):
        """Gets the results from all surveys"""
        return self._storage.items()        
            
    security.declareProtected(TAKE_SURVEY, 'isCorrectAnswer')
    def isCorrectAnswer(self, answer):
        """Is this answer a correct one?"""
        return answer in self.getAnswer()
            
    
class BaseSurveyItemsQuestion(BaseSurveyQuestion):
    """A Basic Question that picks between listed items"""
    __implements__ = BaseContentMixin.__implements__
    security = ClassSecurityInfo()
    schema = BaseSurveyItemsQuestionSchema 
    meta_type = 'BaseSurveyItemsQuestion'
    archetype_name = 'Base Survey Items Question'
    typeDescription= ''
    typeDescMsgId  = ''
    global_allow = 0    
    
    security.declarePublic('getResults')
    def getResults(self):
        """Gets the results from all surveys"""
        values = list(self._storage.values())
        #Below we gather all value actually stored+all default values
        #unique is distributed in CMFPlone        
        vocab = self.unique(values + list(self.field.Vocabulary())) 
        return [( item, values.count(item) ) for item in vocab]